package com.heima.lock;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

@Slf4j
@RestController
@RequestMapping("/sharding")
public class StockController02 {
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;

    /**
     * 分布式锁：基于redis实现
     *
     * @return
     */
    @GetMapping("/stock")
    public String stock1() {
        //1.获取分布式锁,通过setnx向reids中插入一个键值,如果插入成功说明获取到了锁
        Boolean isLock =
                redisTemplate.opsForValue().setIfAbsent("mylock", Thread.currentThread().getId() + "", 30, TimeUnit.SECONDS);
        if (isLock) {
            //获取到锁
            log.info("获取到了分布式锁...");
            try {
                //2.执行业务逻辑
                Integer stock = Integer.valueOf(redisTemplate.opsForValue().get("stock"));
                if (stock > 0) {
                    stock--;
                    redisTemplate.opsForValue().set("stock", stock + "");
                    System.out.println("扣减库存ok,剩余库存:" + stock);

                } else {
                    System.out.println("库存扣减失败,库存不足.....");
                }
            } catch (NumberFormatException e) {
                e.printStackTrace();
            } finally {
                //3.释放自己的锁
                String mylock = redisTemplate.opsForValue().get("mylock");
                if (StringUtils.isNotEmpty(mylock) && String.valueOf(Thread.currentThread().getId()).equals(mylock)) {
                    redisTemplate.delete("mylock");
                }
            }
        } else {
            System.out.println("未获取到分布式锁!");
        }

        return "ok";
    }


    /**
     * 分布式锁：基于redisson实现
     * 阻塞式锁
     * rLock.lock(); // 阻塞锁  默认watch-dog 锁失效时间：30秒,底层基于观察者模式实现锁的自动续期
     *
     * @return
     */
    @GetMapping("/stock2")
    public String stock2() {
        //1.获取锁对象
        RLock rLock = redissonClient.getLock("mylock");
        //通过锁对象尝试加锁 ;锁默认失效时间为30秒 ,如果任务的执行时间较长,会有watchDog 进行时间补偿
        rLock.lock(); // 阻塞锁  默认watch-dog 锁失效时间：30秒,底层基于观察者模式实现锁的自动续期 ;
        try {
            //2.执行业务逻辑代码  ;如果业务代码执行比较耗时 redisson的watchDog会为任务自动续期
            Integer stock = Integer.valueOf(redisTemplate.opsForValue().get("stock"));
            if (stock > 0) {
                stock--;
                System.out.println("扣减库存,剩余:" + stock);
                redisTemplate.opsForValue().set("stock", stock + "");
            } else {
                System.out.println("库存扣减失败- 库存不足!");
            }
        } catch (NumberFormatException e) {
            e.printStackTrace();
        } finally {
            //3.释放锁
            rLock.unlock();
        }


        return "ok";
    }


    /**
     * 分布式锁：基于redisson实现
     * 非阻塞式锁
     * rLock.tryLock();非阻塞锁
     *
     * @return
     */
    @GetMapping("/stock3")
    public String stock3() {
        //1获取锁对象
        RLock rLock = redissonClient.getLock("mylock");

        //2.通过锁对象尝试加锁  ,非阻塞式
        boolean isGetLock = rLock.tryLock();
        if (isGetLock) {
            try {
                //3.获取到锁执行业务逻辑
                // - 扣减库存
                String stock = redisTemplate.opsForValue().get("stock");
                if (StringUtils.isNoneBlank(stock)) {
                    Integer stockValue = Integer.valueOf(stock);
                    if (stockValue > 0) {
                        stockValue--;
                        System.out.println("库存扣减成功,剩余库存:" + stockValue);
                        redisTemplate.opsForValue().set("stock", stockValue + "");
                    } else {
                        System.out.println("库存不足!!!");
                    }
                }
            } catch (NumberFormatException e) {
                e.printStackTrace();
            } finally {
                //4.释放锁
                rLock.unlock();
            }
            return "ok";
        } else {
            //没有获得锁
            return "没有抢到分布式锁...";
        }


    }

}
